<template>
  <!-- 导航栏 -->
    <van-row style="width: 100%">
        <van-sticky style="width: 100%">
            <van-nav-bar ref="refTopBar" :title="currentChatData.title" :style="{width: '100%'}" safe-area-inset-top>
                <template #left>
                    <van-icon @click="handleOpenChatList" name="wap-nav" size="18">列表</van-icon>
                </template>
                <template #right>
                    <van-icon @click="handleAddChat" name="plus" size="18">会话</van-icon>
                </template>
            </van-nav-bar>
        </van-sticky>
    </van-row>

  <!-- 聊天框 -->
    <van-row :style="{width: '100%', background: 'transparent', backgroundSize: 'cover'}">
        <van-pull-refresh
                ref='refPullRefresh'
                v-model="chatMsgData.refreshing"
                @refresh="handleListChatMsg"
                :disabled="chatMsgData.list.length >= chatMsgData.total"
                :style="{width: '100%',
                          height: `${chatBoxHeight}px`,
                          overflow: 'auto'}">
            <van-list
                    :disabled="true"
                    ref="refChatMsgList"
                    :style="{paddingBottom: '20px'}">
                <span v-for="(e, i) in chatMsgData.reverseList" :key="e.id">
                    <chat-bubble
                            :chat-msg="e"
                            :imgUrl="store.state.user.avatar"
                            :content="e.prompt"
                            :position="'right'"
                    />
                    <chat-bubble
                            :is-last-msg="chatMsgData.list.length - 1 === i"
                            :chat-msg="e"
                            :imgUrl="aiAvatar"
                            :content="e.reply"
                            :position="'left'"
                            :reply-place-holder="prompt.replyPlaceHolder"
                            @stopAnswer="handleStopAnswer"
                            @retryAnswer="handleRetryAnswer"
                    />
                </span>
            </van-list>
        </van-pull-refresh>
    </van-row>

  <!-- 输入区域 -->
    <van-row ref="refPromptInputRow" style="width: 100%;">
        <van-field
                v-model="prompt.input"
                rows="1"
                :autosize="{ minHeight: 24, maxHeight: 72 }"
                type="textarea"
                maxlength="300"
                show-word-limit
                @input="handlePromptInput"
                :placeholder="prompt.placeHolder">
            <template #right-icon>
                <van-button
                        style="width: 64px; height: 32px"
                        icon="guide-o"
                        type="primary"
                        @click="handleClickSendBtn"
                        size="small"
                        :disabled="!store.getters['user/isLogin']
                        || store.state.common.hasSseClient
                        || isStrEmpty(prompt.input)
                        || !store.getters['user/canSendPrompt']"
                >
                    发送
                </van-button>
            </template>
        </van-field>
    </van-row>

  <!-- 左侧弹出 -->
    <chat-list :open="chatListData.show"
               @close="chatListData.show = false"
               @click-chat="handleClickChat"
               @update-chat="handleUpdateChat"
               @remove-chat="handleRemoveChat"
               @remove-all-chat="handleRemoveAllChat"/>
</template>

<script setup>

import ChatList from "@/views/chat/chatList.vue";
import {computed, nextTick, onMounted, reactive, ref, watchPostEffect} from "vue";
import ChatBubble from "@/views/chat/chatBubble.vue";
import store from "@/store";
import {EventSourcePolyfill} from "event-source-polyfill";
import {addChatMessage, listChatMessage} from "@/server/chat/chatMessage";
import {isAiReplying} from "@/utils/aiChat";
import {isStrEmpty} from "@/utils/stringUtil";

// 引入dom元素
const refTopBar = ref(null)
const refPromptInputRow = ref(null)
const refPullRefresh = ref(null)
const refChatMsgList = ref(null)

onMounted(() => {
    // 顶部导航栏高度
    topTabHeight.value = refTopBar.value.$el.offsetHeight
    // 加载后立马更新一次聊天区域高度
    handlePromptInput()

    // console.log(`可视窗口高度：${window.innerHeight}px`)
    // console.log(`顶部导航栏高度：${topTabHeight.value}px`)
    // console.log(`输入区域高度：${promptInputHeight.value}px`)
    // console.log(`底部导航栏高度：${store.state.common.bottomBarHeight}`);
})

// 监听输入区域高度，动态修改聊天框高度
watchPostEffect(() => {
    // 设置聊天框高度
    chatBoxHeight.value = store.state.common.windowInnerHeight - topTabHeight.value - store.state.common.bottomBarHeight - promptInputHeight.value

    // 聊天框组件改动渲染完后，滚动条下拉到底
    nextTick(() => {
        scrollToBottom()
    })
})

// 静态资源
// ai头像
const aiAvatar = ref(require('@/assets/img/chatAIAvatarc.png'))

// 输入区域高度（px）
const promptInputHeight = ref(0)
// 顶部导航栏高度（px）
const topTabHeight = ref(0)
// 聊天区域高度（px）
const chatBoxHeight = ref(0)

// 输入内容
const prompt = reactive({
    input: null,
    placeHolder: '想和我聊点什么吗？',
    replyPlaceHolder: '正在思考中……',
})

watchPostEffect(() => {
    prompt.placeHolder = store.state.common.supportSSE
        ? store.getters['user/canSendPrompt'] ? '想和我聊点什么吗？' : '体验次数已耗尽'
        : '当前浏览器不兼容，请更换为IE、EDGE之外的浏览器'
})

// 当前会话
const currentChatData = reactive({
    id: null,
    title: '新的会话'
})
// 当前消息
let currentChatMsgData = reactive({})

// 聊天记录
const chatMsgData = reactive({
    refreshing: false,
    disablePullRefresh: false,
    pageNum: 0,
    pageSize: 10,
    total: 0,
    // 聊天记录
    list: [],
    // 翻转聊天记录
    reverseList: computed(() => {
        // 浅拷贝
        return [...chatMsgData.list].reverse()
    })
})
// 会话列表
const chatListData = reactive({
    show: false
})

// 监听输入事件
const handlePromptInput = () => {
    promptInputHeight.value = refPromptInputRow.value.$el.offsetHeight
}

// 滚动至聊天框底部
const scrollToBottom = () => {
    let pullRefresh = refPullRefresh.value.$el;
    let scrollHeight = pullRefresh.offsetHeight;
    // console.log(`scrollHeight：${scrollHeight}`);
    let contentHeight = refChatMsgList.value.$el.offsetHeight;
    // console.log(`contentHeight：${contentHeight}`);

    // gdk 不起效，原因暂时未知，和auto一样无法下拉到底
    // pullRefresh.scrollTop = contentHeight - scrollHeight

    // behavior
    // "auto"：浏览器自动决定最佳的滚动行为。这是默认值。
    // "smooth"：平滑滚动到指定位置。
    pullRefresh.scrollTo({
        top: (contentHeight - scrollHeight) * 200,
        left: 0,
        behavior: 'smooth'
    })
}

// 处理会话修改事件
const handleUpdateChat = (chat) => {
    if (chat.id === currentChatData.id) {
        currentChatData.title = chat.title
    }
}
// 处理会话删除事件
const handleRemoveChat = (chat) => {
    if (chat.id === currentChatData.id) {
        resetCurrentChat(null, '新的会话')
    }
}
// 处理会话全部删除事件
const handleRemoveAllChat = () => {
    resetCurrentChat(null, '新的会话')
}

// 打开会话列表
const handleOpenChatList = () => {
    if (isAiReplying()) {
        return
    }

    chatListData.show = true
}

// 新建会话
const handleAddChat = () => {
    if (isAiReplying()) {
        return
    }

    resetCurrentChat(null, '新的会话')
}

// 重置当前会话
const resetCurrentChat = (id, title) => {
    currentChatData.id = id
    currentChatData.title = title

    chatMsgData.list = []
    chatMsgData.pageNum = 0
    chatMsgData.total = 0
}

// 会话列表点击会话
const handleClickChat = (chat) => {
    // 已经是当前会话，不在重新加载
    if (currentChatData.id === chat.id) {
        return
    }

    // 重置会话
    resetCurrentChat(chat.id, chat.title)
    // 查询会话消息
    handleListChatMsg(true)
}

// 处理下拉加载会话消息
const handleListChatMsg = (toBottom) => {
    // 没有会话id不查询
    if (!currentChatData.id) {
        chatMsgData.total = 0
        return
    }

    const condition = {
        chatId: currentChatData.id,
        pageNum: ++chatMsgData.pageNum,
        pageSize: chatMsgData.pageSize
    }
    listChatMessage(condition).then(res => {
        chatMsgData.list = chatMsgData.list.concat(res.rows)
        chatMsgData.total = res.total

        // 更新后滚动到底部
        if (toBottom) {
            nextTick(() => {
                scrollToBottom()
            })
        }
    }).finally(() => {
        chatMsgData.refreshing = false
    })
}

// 点击发送按钮
const handleClickSendBtn = () => {
    // 当前已有sse连接
    if (isAiReplying()) {
        return
    }
    store.commit('common/update', {hasSseClient: true})

    currentChatMsgData = reactive({
        id: null,
        chatId: currentChatData.id,
        model: null,
        prompt: prompt.input,
        reply: null,
        retryCount: 0
    })

    // 获取回答
    handleGetAnswer('add')
}

// 停止回答
const handleStopAnswer = () => {
    let sseClient = store.state.common.sseClient;
    if (!sseClient) {
        return
    }

    try {
        sseClient.close()
    } catch (ignore) {
    }

    // 保存回答
    handleAddChatMsg()
}

// 重新回答
const handleRetryAnswer = (chatMsg) => {
    // 当前已有sse连接
    if (isAiReplying()) {
        return
    }
    store.commit('common/update', {hasSseClient: true})

    currentChatMsgData = chatMsg
    // 获取回答
    handleGetAnswer('retry')
}

// 发送消息 type add新的提问、retry重新回答
const handleGetAnswer = (type) => {
    currentChatMsgData.chatId = currentChatData.id
    // 重置回复
    currentChatMsgData.reply = prompt.replyPlaceHolder

    // 创建客户端
    const url = `http://127.0.0.1:8080/ai/chat/sse?prompt=${encodeURIComponent(currentChatMsgData.prompt)}&chatId=${currentChatData.id || ''}&chatMessageId=${currentChatMsgData.id || ''}`
    let sse = new EventSourcePolyfill(url, {
        headers: {
            Authorization: "Bearer " + store.state.user.token,
        },
        timeout: 20000
    });

    // 如果是新的提问
    if ('add' === type) {
        // 添加消息对象到头部
        chatMsgData.list.unshift(currentChatMsgData)
        // 清空输入区域
        prompt.input = null
        nextTick(() => {
            scrollToBottom()
        })
    }

    sse.onopen = (event) => {
        sse = event.target;
        store.commit('common/update', {sseClient: sse})
        // console.log("开始输出后端返回值：" + JSON.stringify(event));
    };

    sse.onmessage = (event) => {
        // {"type":"message","target":{"_listeners":{},"url":"http://127.0.0.1:8080/ai/chat/sse?prompt=随便聊两句","readyState":1,"withCredentials":false,"headers":{"authorization":"Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjUyZmEyNmEwLWQ5NGQtNDU4MS04MTVjLTI3MTRiZDVmMzE0NiJ9.pGO3-4kqD9U-Q1tzSF_nB1ualHRmY3Sm-tnPtE6gy2E7FCQ09a4ZGbZ2u8Gwwbd3zLMRqgaN-NWKu0hdi9hfCg"}},"data":"为","lastEventId":""}
        /*if (event.lastEventId == "[TOKENS]") {
            text = text + event.data;
            this.content = text
            text = ''
            return;
        }*/

        // 后端报错通知
        if (event.lastEventId === 'notify-error') {
            // console.log('notify-error')
            event.target.close();
            currentChatMsgData.reply = event.data
            // 清空sse连接
            store.commit('common/clearSse')
            // 组件更新后，滚动到最底部
            nextTick(() => {
                scrollToBottom()
            })
            return;
        }

        // 后端更新试用次数通知
        if (event.lastEventId === 'notify-update-trial') {
            store.commit('user/update', {trialNum: +event.data})
            return;
        }

        // console.log("输出后端返回值：" + JSON.stringify(event));

        // 监听到结束标志
        if (event.data === '[DONE]') {
            if (sse) {
                sse.close();
                handleAddChatMsg()
            }
            return;
        }

        if (!event.data) {
            return;
        }
        let content = JSON.parse(event.data).content;
        if (!content || content === 'null') {
            return;
        }

        // 接收到的第一个消息
        if (prompt.replyPlaceHolder === currentChatMsgData.reply) {
            currentChatMsgData.reply = content
        } else {
            currentChatMsgData.reply += content
        }

        // 组件更新后，滚动到最底部
        nextTick(() => {
            scrollToBottom()
        })
    };

    sse.onerror = (event) => {
        /*console.log("onerror", event);
        if (event.readyState === EventSource.CLOSED) {
            console.log('connection is closed');
        } else {
            console.log("Error occured", event);
        }*/

        event.target.close();
        currentChatMsgData.reply = '网络连接异常，请重试'
        // 清空sse连接
        store.commit('common/clearSse')
        // 组件更新后，滚动到最底部
        nextTick(() => {
            scrollToBottom()
        })
    };
}

// AI回复完消息后保存消息
const handleAddChatMsg = () => {
    addChatMessage(currentChatMsgData).then(res => {
        const data = res.data;

        // 是新会话，重置会话
        if (data.chatId !== currentChatMsgData.chatId) {
            currentChatMsgData.chatId = data.chatId

            resetCurrentChat(data.chatId, data.title)
            chatMsgData.list.push(currentChatMsgData)
        }

        // 是新消息，记录会话消息id
        if (data.msgId !== currentChatMsgData.id) {
            currentChatMsgData.id = data.msgId
        }

    }).finally(() => {
        // 清空sse连接
        store.commit('common/clearSse')
    })
}

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
</style>
